#include <stdio.h>
#include <string.h>
#include <stdlib.h>

void PrintHelp();
void PrintVersion();
void PrintTooManyParamError();
void PrintParameterError(char* param);
void PrintFileError(char* filename);
int decodeProcess(const char* filein,const char* fileou);
int encodeProcess(const char* input, const char* output, int lineLength);

int main(int argc, char* argv[])
{
    char showVersion = 0;
    char showHelp = 0;
    char showUnknownParam=0;
    char expectingInput = 0;
    char expectingOutput = 0;
    char expectingLength= 0;
    char* action = 0;
    char* input = 0;
    char* output = 0;
    int length = 0;

    int numArg = 1;

    while (numArg < argc && !showVersion && !showHelp && !showUnknownParam){
    	char* param = argv[numArg];
		int paramLength = strlen(param);

		if (param[0] == '-')
		{
			if (paramLength == 2)
			{
				if (param[1] == 'v' || param[1] == 'V')
					showVersion = 1;
				else if (param[1] == 'h' || param[1] == 'H')
					showHelp = 1;
				else if (param[1] == 'x' || param[1] == 'X' || param[1] == 'b' || param[1] == 'B'){
					action=param;
				}
				else if (param[1] == 'i' || param[1] == 'I')
					expectingInput = 1;
				else if (param[1] == 'o' || param[1] == 'O')
					expectingOutput = 1;
				else if (param[1] == 'l' || param[1] == 'L')
					expectingLength = 1;
				else{
					PrintParameterError(param);
					showUnknownParam = 1;
				}

			}
			else if (paramLength == 1 && (expectingInput || expectingOutput))
			{
				expectingInput = 0;
				expectingOutput = 0;
			}
			else if (strcmp(param, "--version") == 0)
				showVersion = 1;
			else if (strcmp(param, "--help") == 0)
				showHelp = 1;
			else if (strcmp(param, "--hexdump") == 0 || strcmp(param, "--binary")){
				action=param;
			}
			else if (strcmp(param, "--input") == 0)
				expectingInput = 1;
			else if (strcmp(param, "--output") == 0)
				expectingOutput = 1;
			else if (strcmp(param, "--length") == 0)
				expectingLength = 1;
			else{
				PrintParameterError(param);
				showUnknownParam = 1;

			}

		}
		else if (expectingInput)
		{
			input = param;
			expectingInput = 0;
		}
		else if (expectingOutput)
		{
			output = param;
			expectingOutput = 0;
		}
		else if (expectingLength)
		{
			char* len=malloc(7*sizeof(char));
			strcpy(len,param);
			length = atoi(len);
			expectingLength = 0;
		}
		else
			showHelp = 1;

    	numArg++;
    }

    if (expectingInput || expectingOutput)
    	showHelp = 1;

    printf("\n");

    if (showVersion){
    	if (argc>2){
    		PrintTooManyParamError();
			PrintHelp();
    	} else {
    		PrintVersion();
    	}
    }
    else if (showHelp){
    	if (argc>2){
    		PrintTooManyParamError();
    	}
    	PrintHelp();
    } else if (showUnknownParam){
    	PrintHelp();
    }
    else
    {
        int error = 0;

        if (!input)
            input = "stdin";

        if (!output)
        	output = "stdout";

        if (!action || ((strcmp(action, "--hexdump") == 0) || (strcmp(action, "-x") == 0)))
            error = encodeProcess(input,output, length);
        else if (action && ((strcmp(action, "--binary") == 0) || (strcmp(action, "-b") == 0))){
        	error = decodeProcess(input,output);
        }
        else
        {
            fprintf(stderr, "Unknown action");
            return 1;
        }
        if (error==-1){
        	PrintFileError(input);
        } else {
        	if (error==-2){
        		PrintFileError(output);
        	}
        }

        return error;
    }

    return 0;
}

int encodeProcess(const char* filein,const char* fileou, int lineLength) {
        FILE *fdi, *fdo;
        int i=0, c;

        fdi=strcmp(filein,"stdin") ?fopen(filein,"rb"):stdin;
	if (!fdi) return -1; /* Error while opening input file */
        fdo=strcmp(fileou,"stdout")?fopen(fileou,"wt"):stdout;
	if (!fdo) return -2; /* Error while opening output file */

        while ((c=fgetc(fdi))!=EOF) {
          fprintf(fdo,"%02x",c);
          if (lineLength && !((++i)%lineLength))
                fputc('\n',fdo);
         }
        if (!lineLength || i%lineLength)
                fputc('\n',fdo);
	fclose(fdi);
	fclose(fdo);
	return 0; /*Successfully finished*/
}

int decodeProcess(const char* filein,const char* fileou) {
	FILE *fdi, *fdo;
	int  c;

	fdi=strcmp(filein,"stdin") ?fopen(filein,"rb"):stdin;
	if (!fdi) return -1; /* Error while opening input file */
	fdo=strcmp(fileou,"stdout")?fopen(fileou,"wt"):stdout;
	if (!fdo) return -2; /* Error while opening output file */

	while (fscanf(fdi,"%02x", &c)!=EOF) {
		fputc(c,fdo);
	}

	fclose(fdi);
	fclose(fdo);
	
	return 0;
}

void PrintTooManyParamError()
{
    fprintf(stderr,"Error: too many parameters\n\n");
}

void PrintParameterError(char* param)
{
    fprintf(stderr,"Error: unknown parameter '%s' \n\n",param);
}

void PrintFileError(char* filename)
{
    fprintf(stderr,"Error: Impossible to open file '%s' \n\n",filename);
}

void PrintVersion(){
	printf("-- TP0 --\n");
	printf("Organización de Computadoras - 66.20\n");
	printf("Version 1.0\n");
}


void PrintHelp()
{
    printf("Usage:\n");
    printf(" tp0 -h\n");
    printf(" tp0 -V\n");
    printf(" tp0 [options]\n");
    printf("Options:\n");
    printf(" -h, --help \t Print this information.\n");
    printf(" -V, --version \t Print version and quit.\n");
    printf(" -x, --hexdump \t Hexdump mode: encode the input (binary file) in hexadecimal format (default).\n");
    printf(" -b, --binary \t Binary mode: decode the input (hexdump file) in binary format.\n");
    printf(" -i, --input \t Path to input file.\n");
    printf(" -o, --output \t Path to the output file.\n");
    printf(" -l, --length \t Number of hex bytes per line (line length) at the output in hexdump mode ( 0 = default = no limit ).\n");

    printf("Examples:\n");
    printf(" tp0\n");
    printf(" tp0 -i -\n");
    printf(" tp0 -i - -o - -x \n");
    printf(" tp0  b\n");
    printf(" tp0 -i /dev/random -o /tmp/random-hexdump -l 16\n");
}
